﻿/*-------------------------------------------------------------------------
* 命名空间: ScreenCapture.Common/SingleInstanceManager
* 类       名: SingleInstanceManager
* 功      能:  应用程序单例管理类
* 时      间:  2023/12/1 10:13:22
* 版      本:  V1.0
* 作      者:  Fioman
* 格      言:  Talk is cheap,show me the code ^_^
*-------------------------------------------------------------------------*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ScreenCapture.Common
{
    /// <summary>
    /// 应用程序的单实例管理,如果用户尝试启动已经运行的程序的另外一个实例,
    /// 它将把已经运行的程序实例带到前台,而不是启动新的实例.
    /// </summary>
    public static class SingleInstanceManager
    {
        // 用于同步多线程的访问,防止同时启动多个应用程序的实例,可以称之为互斥体对象
        private static Mutex? _mutex;
        private static Task? _task;

        // 创建Mutex的标识,用来唯一标识这个Mutex,只要创建的时候使用了这个Id,就代表是同一个Mutex
        private const string _mutextId = "screenCapture-mutex-2023-12-01-Fioman-start";

        // 用于创建EventWaitHanle的唯一标识字符串,只要创建EventWaitHandle的时候的这个字符串相等
        // 就代表是同一个EventWaitHandle
        private const string _eventWaittHandleId = "screenCapture-mutex-2023-12-01-Fioman-start";

        public static void SingleInstanceCheck()
        {
            // 创建一个线程互斥体对象,各个参数的解释:
            /*
             * 1) true: 
             * 调用线程是否拥有新创建的互斥体的所有权,true表示创建Mutex的线程直接拥有Mutex的所有权.
             * 2) _mutexId
             * 一个字符串,用于为Mutex实例创建一个系统范围内的名称.这个名称在全局范围内是唯一的,这样其他的
             * 线程或者进程可以通过这个名称找到并使用这个Mutex.
             * 3) out bool CreatedNew
             * 输出参数,当这个函数返回时,如果当前的互斥体已经存在,就证明程序已经开启了,CreatedNew返回False
             * 如果说CreatedNew 为True,表示还没有开启程序,就后续来开启程序即可
             */
            _mutex = new Mutex(true, _mutextId, out bool CreatedNew);
            if (!CreatedNew)
            {
                // 如果程序已经开启,就是没有创建新的互斥体,创建一个EventWaitHanle(事件等待句柄)
                var handle = CreateWaitHandle();
                // 然后给这个事件发送一个信号,这个信号应该是用来恢复前台线程,并做激活任务的.
                handle.Set(); // 设定事件等待句柄为有信号状态可以这么理解发送一个事件信号
                // 退出当前的应用
                Environment.Exit(0);
            }
        }

        public static EventWaitHandle CreateWaitHandle()
        {
            /*
             * 各个参数的解释:
             * 1) false
             * EventWaitHandle的初始状态,如果true表示有信号状态,就是试图等待这个信号的会立即执行,不会阻塞.
             * 如果为false,表示是无信号状态,试图等待这个信号的会阻塞等待,直到下一个信号到来为止.
             * 2) EventResetMode.AutoReset
             * 就是自动复位模式,当一个线程收到信号是,这个信号会自动复位为无信号,如果不设置成这样,然后
             * 不手动的复位这个信号,那么所有的等待这个信号的地方都会收到这个信号,并且这个信号会一直存在
             * 3) EventWaitHandle的全局唯一标识名称,所有的线程都是通过这个Id来访问同一个EventWaitHandle
             * 实例的
             */
            return new EventWaitHandle(false, EventResetMode.AutoReset, _eventWaittHandleId);
        }

    }
}
